package manager;

import dao.FileDao;
import dao.FileMeta;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author: cheng
 * @date: 2023/7/5 20:08
 * @description: 扫描目录,更新到数据库中
 */
public class FileManager {
    private FileDao fileDao = new FileDao();

    //初始数目为1,当线程池调用完所有任务之后,就调用一次 countDown 进行撞线
    private CountDownLatch countDownLatch = null;

    //计数器
    private AtomicInteger count = new AtomicInteger(0);
    //扫描目录(包含子目录),并保存到数据库中
    public void scanAll(File basePath){
        System.out.println("[FileManager] scanAll 开始");
        long ben = System.currentTimeMillis();

        //每次调用scanAll都重新创建countDownLatch
        countDownLatch = new CountDownLatch(1);

        scanAllThreadPoll(basePath);

        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        long end = System.currentTimeMillis();
        System.out.println("[FileManager] scanAll 结束,执行时间共 "+(end - ben)+"ms");
    }

    //单线程执行
    private void scanAllOneThread(File path){
        if (!path.isDirectory()){
            return;
        }
        //扫描当前目录
        scan(path);
        //当前目录所有文件
        File[] files = path.listFiles();
        if (files == null || files.length == 0){
            return;//当前目录为空
        }
        for (File f : files){
            if(f.isDirectory()){
                //目录
                scanAllOneThread(f);
            }
        }
    }

    private static ExecutorService executorService = Executors.newFixedThreadPool(8);
    //使用线程池进行多线程扫描
    private void scanAllThreadPoll(File basePath){
        if (!basePath.isDirectory()){
            return;
        }

        count.getAndIncrement();//后置++

        //在线程池进行扫描
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                //防止因scan异常导致后置--执行不到
                try {
                    scan(basePath);
                }finally {
                    count.getAndDecrement();//后置--
                    if (count.get() == 0){
                        countDownLatch.countDown();
                    }
                }
            }
        });
        //递归其他目录
        File[] files = basePath.listFiles();
        if (files == null || files.length == 0){
            return;//当前目录为空
        }
        for (File f : files){
            if(f.isDirectory()){
                //目录
                scanAllThreadPoll(f);
            }
        }
    }


    //针对一个目录进行扫描,不考虑子目录里的数据
    //scanned 是文件系统的真实目录,saved 是数据库里的,一般而言初始情况下 saved 有可能是是空的,而 scanned 里有内容
    //后续 saved 可能有些内容,其中 scanned 和 saved 有可能一样,有可能不一样(用户修改了真实目录中的内容)
    // scanned 有 saved没有,则要将没的内容插入到数据库中,反之亦然
    private void scan(File path){
        System.out.println("[FileManager] 扫描路径: " + path.getAbsolutePath());
        //列出文件系统上的真实文件/目录
        List<FileMeta> scanned = new ArrayList<>();
        File[] files = path.listFiles();
        if(files != null){
            for (File f: files) {
                scanned.add(new FileMeta(f));
            }
        }

        //数据库指定目录的内容
        List<FileMeta> saved = fileDao.searchByPath(path.getPath());

        //找出文件中没的,数据库中有的,进行删除
        List<FileMeta> filDelete = new ArrayList<>();
        for(FileMeta fileMeta : saved){
            if(!scanned.contains(fileMeta)){//注意contains要调用equals方法(默认比较对象地址) 因此需要重写
                //把数据库有的文件中没的,拿出来存储在 filDelete 中
                filDelete.add(fileMeta);
            }
        }
        fileDao.delete(filDelete);

        //找出文件中有的,数据库中没的,进行添加
        List<FileMeta> filAdd = new ArrayList<>();
        for(FileMeta fileMeta : scanned){
            if(!saved.contains(fileMeta)){
                filAdd.add(fileMeta);
            }
        }
        fileDao.add(filAdd);
    }
}
